package com.sojson.config.qps.sentinel;

import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.context.annotation.Configuration;

import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource;
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.sojson.util.cache.CacheUtil;
import com.sojson.util.cache.redis.RedisCacheUtilInterface;
import com.sojson.util.cache.redis.bean.DbRds;

import lombok.extern.slf4j.Slf4j;

/**
 * Sentinel配置类
 * 
 * @author liu
 * @date 2021-08-20
 */
@Slf4j
@Configuration
public class CustomSentinelConfig {

    public final String FIELD_CHANNEL = "Channel";
    // 限流规则key前缀
    public final String RULE_FLOW = "SentinelRuleFlow";
    public final String RULE_FLOW_CHANNEL = RULE_FLOW + FIELD_CHANNEL;
    // 熔断规则key前缀
    public final String RULE_DEGRADE = "SentinelRuleDegrade";
    public final String RULE_DEGRADE_CHANNEL = RULE_DEGRADE + FIELD_CHANNEL;
    // 热点规则key前缀
    public final String RULE_PARAM_FLOW = "SentinelRuleParamFlow";
    public final String RULE_PARAM_FLOW_CHANNEL = RULE_PARAM_FLOW + FIELD_CHANNEL;
    // 系统规则key前缀
    public final String RULE_SYSTEM = "SentinelRuleSystem";
    public final String RULE_SYSTEM_CHANNEL = RULE_SYSTEM + FIELD_CHANNEL;
    // 授权规则key前缀
    public final String RULE_AUTHORITY = "SentinelRuleAuthority";
    public final String RULE_AUTHORITY_CHANNEL = RULE_AUTHORITY + FIELD_CHANNEL;

    /**
     * 获取Key全称
     * 
     * @param key
     * @return
     */
    private String getKey(String key) {
        return CacheUtil.getKeyBase(key);
    }

    /**
     * 获取Key全称
     * 
     * @param key
     * @return
     */
    private String getAppName(String key) {
        return getKey(key + SentinelConfig.getAppName());
    }

    @PostConstruct
    public void init() {
        log.info("正在初始化Sentinel");

        DbRds dbRds = RedisCacheUtilInterface.dbs.get(0);
        RedisConnectionConfig config =
            RedisConnectionConfig.builder().withHost(dbRds.getHost()).withPort(dbRds.getPort())
                .withPassword(dbRds.getPassword()).withDatabase(RedisCacheUtilInterface.dbIndex).build();
        log.info("Sentinel数据源配置完成");

        // 配置限流规则
        Converter<String, List<FlowRule>> parser =
            source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
        ReadableDataSource<String, List<FlowRule>> redisDataSourceFlow =
            new RedisDataSource<>(config, getAppName(RULE_FLOW), getKey(RULE_FLOW_CHANNEL), parser);
        FlowRuleManager.register2Property(redisDataSourceFlow.getProperty());
        log.info("Sentinel限流规则配置完成");

        // 配置熔断规则
        Converter<String, List<DegradeRule>> parserDegrade =
            source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {});
        ReadableDataSource<String, List<DegradeRule>> redisDataSourceDegrade =
            new RedisDataSource<>(config, getAppName(RULE_DEGRADE), getKey(RULE_DEGRADE_CHANNEL), parserDegrade);
        DegradeRuleManager.register2Property(redisDataSourceDegrade.getProperty());
        log.info("Sentinel熔断规则配置完成");

        // 配置热点规则
        Converter<String, List<ParamFlowRule>> parserParamFlowRule =
            source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {});
        ReadableDataSource<String, List<ParamFlowRule>> redisDataSourceParamFlow = new RedisDataSource<>(config,
            getAppName(RULE_PARAM_FLOW), getKey(RULE_PARAM_FLOW_CHANNEL), parserParamFlowRule);
        ParamFlowRuleManager.register2Property(redisDataSourceParamFlow.getProperty());
        log.info("Sentinel热点规则配置完成");

        // 配置系统规则
        Converter<String, List<SystemRule>> parserSystem =
            source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() {});
        ReadableDataSource<String, List<SystemRule>> redisDataSourceSystem =
            new RedisDataSource<>(config, getAppName(RULE_SYSTEM), getKey(RULE_SYSTEM_CHANNEL), parserSystem);
        SystemRuleManager.register2Property(redisDataSourceSystem.getProperty());
        log.info("Sentinel系统规则配置完成");

        // 配置授权规则
        Converter<String, List<AuthorityRule>> parserAuthorityRule =
            source -> JSON.parseObject(source, new TypeReference<List<AuthorityRule>>() {});
        ReadableDataSource<String, List<AuthorityRule>> redisDataSourceAuthority = new RedisDataSource<>(config,
            getAppName(RULE_AUTHORITY), getKey(RULE_AUTHORITY_CHANNEL), parserAuthorityRule);
        AuthorityRuleManager.register2Property(redisDataSourceAuthority.getProperty());
        log.info("Sentinel授权规则配置完成");

        log.info("初始化Sentinel结束");
    }

}